/*
 * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package jdk.io;

import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

/**
 * <code>BufferedInputStream</code> 为另一个输入流添加了功能——即缓冲输入
 * 和支持 <code>mark</code> 和 <code>reset</code> 方法的能力。
 * 创建 <code>BufferedInputStream</code> 时，会创建一个内部缓冲区数组。
 * 当从流中读取或跳过字节时，内部缓冲区会根据需要从包含的输入流中
 * 一次性重新填充许多字节。<code>mark</code> 操作记住输入流中的一个点，
 * <code>reset</code> 操作导致在从包含的输入流中获取新字节之前，
 * 重新读取自最近的 <code>mark</code> 操作以来读取的所有字节。
 *
 * @author  Arthur van Hoff
 * @since   JDK1.0
 */
public
class BufferedInputStream extends FilterInputStream {

    private static int DEFAULT_BUFFER_SIZE = 8192;

    /**
     * 要分配的数组的最大大小。
     * 某些 VM 在数组中保留一些头部字。
     * 尝试分配更大的数组可能会导致 OutOfMemoryError：请求的数组大小超过 VM 限制
     */
    private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;

    /**
     * 存储数据的内部缓冲区数组。必要时，它可以被另一个不同大小的数组替换。
     */
    protected volatile byte buf[];

    /**
     * 为 buf 提供 compareAndSet 的原子更新器。这是必要的，因为关闭可以是异步的。
     * 我们使用 buf[] 的空值作为主要指示符，表明此流已关闭。（"in" 字段在关闭时也会被置空。）
     */
    private static final
        AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
        AtomicReferenceFieldUpdater.newUpdater
        (BufferedInputStream.class,  byte[].class, "buf");

    /**
     * 缓冲区中最后一个有效字节的索引加一的索引。
     * 此值始终在 <code>0</code> 到 <code>buf.length</code> 的范围内；
     * 元素 <code>buf[0]</code> 到 <code>buf[count-1]</code> 包含从底层输入流获取的缓冲输入数据。
     */
    protected int count;

    /**
     * 缓冲区中的当前位置。这是要从 <code>buf</code> 数组中读取的下一个字符的索引。
     * <p>
     * 此值始终在 <code>0</code> 到 <code>count</code> 的范围内。
     * 如果它小于 <code>count</code>，则 <code>buf[pos]</code> 是要作为输入提供的下一个字节；
     * 如果它等于 <code>count</code>，则下一个 <code>read</code> 或 <code>skip</code> 操作
     * 将需要从包含的输入流中读取更多字节。
     *
     * @see     java.io.BufferedInputStream#buf
     */
    protected int pos;

    /**
     * 上次调用 <code>mark</code> 方法时 <code>pos</code> 字段的值。
     * <p>
     * 此值始终在 <code>-1</code> 到 <code>pos</code> 的范围内。
     * 如果输入流中没有标记位置，则此字段为 <code>-1</code>。
     * 如果输入流中有标记位置，则在 <code>reset</code> 操作后，
     * <code>buf[markpos]</code> 是要作为输入提供的第一个字节。
     * 如果 <code>markpos</code> 不为 <code>-1</code>，
     * 则所有从位置 <code>buf[markpos]</code> 到 <code>buf[pos-1]</code> 的字节
     * 必须保留在缓冲区数组中（尽管它们可能会被移动到缓冲区数组中的另一个位置，
     * 并对 <code>count</code>、<code>pos</code> 和 <code>markpos</code> 的值进行适当调整）；
     * 除非 <code>pos</code> 和 <code>markpos</code> 之间的差值超过 <code>marklimit</code>，
     * 否则不得丢弃它们。
     *
     * @see     java.io.BufferedInputStream#mark(int)
     * @see     java.io.BufferedInputStream#pos
     */
    protected int markpos = -1;

    /**
     * 在调用 <code>mark</code> 方法后，在后续调用 <code>reset</code> 方法失败之前允许的最大预读量。
     * 每当 <code>pos</code> 和 <code>markpos</code> 之间的差值超过 <code>marklimit</code> 时，
     * 可以通过将 <code>markpos</code> 设置为 <code>-1</code> 来丢弃标记。
     *
     * @see     java.io.BufferedInputStream#mark(int)
     * @see     java.io.BufferedInputStream#reset()
     */
    protected int marklimit;

    /**
     * 确保底层输入流未被关闭操作置空；若未关闭则返回该流。
     */
    private InputStream getInIfOpen() throws IOException {
        InputStream input = in;
        if (input == null)
            throw new IOException("Stream closed");
        return input;
    }

    /**
     * 确保缓冲区未被关闭操作置空；若未关闭则返回该缓冲区。
     */
    private byte[] getBufIfOpen() throws IOException {
        byte[] buffer = buf;
        if (buffer == null)
            throw new IOException("Stream closed");
        return buffer;
    }

    /**
     * 创建 <code>BufferedInputStream</code>
     * 并保存其参数——输入流 <code>in</code>，以供后续使用。
     * 同时创建一个内部缓冲区数组并存储在 <code>buf</code> 中。
     *
     * @param   in   底层输入流。
     */
    public BufferedInputStream(InputStream in) {
        this(in, DEFAULT_BUFFER_SIZE);
    }

    /**
     * 使用指定缓冲区大小创建 <code>BufferedInputStream</code>，
     * 并保存其参数——输入流 <code>in</code>，以供后续使用。
     * 同时创建长度为 <code>size</code> 的内部缓冲区数组并存储在 <code>buf</code> 中。
     *
     * @param   in     底层输入流。
     * @param   size   缓冲区大小。
     * @exception IllegalArgumentException 如果 {@code size <= 0}。
     */
    public BufferedInputStream(InputStream in, int size) {
        super(in);
        if (size <= 0) {
            throw new IllegalArgumentException("Buffer size <= 0");
        }
        buf = new byte[size];
    }

    /**
     * 向缓冲区填充更多数据，同时考虑重排及处理标记的其他技巧。
     * 假定该方法由同步方法调用，并假定所有数据均已读取完毕，即 pos > count。
     */
    private void fill() throws IOException {
        byte[] buffer = getBufIfOpen();
        if (markpos < 0)
            pos = 0;            /* 无标记：丢弃缓冲区 */
        else if (pos >= buffer.length)  /* 缓冲区中没有剩余空间 */
            if (markpos > 0) {  /* 可以丢弃缓冲区的早期部分 */
                int sz = pos - markpos;
                System.arraycopy(buffer, markpos, buffer, 0, sz);
                pos = sz;
                markpos = 0;
            } else if (buffer.length >= marklimit) {
                markpos = -1;   /* 缓冲区变得太大，使标记无效 */
                pos = 0;        /* 丢弃缓冲区内容 */
            } else if (buffer.length >= MAX_BUFFER_SIZE) {
                throw new OutOfMemoryError("Required array size too large");
            } else {            /* 扩展缓冲区 */
                int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
                        pos * 2 : MAX_BUFFER_SIZE;
                if (nsz > marklimit)
                    nsz = marklimit;
                byte nbuf[] = new byte[nsz];
                System.arraycopy(buffer, 0, nbuf, 0, pos);
                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {
                    // 如果有异步关闭，则无法替换缓冲区。
                    // 注意：如果 fill() 方法被多线程访问，则需要更改此处。
                    // 但目前，CAS 失败的唯一方式是通过关闭操作。
                    // assert buf == null;
                    throw new IOException("Stream closed");
                }
                buffer = nbuf;
            }
        count = pos;
        int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)
            count = n + pos;
    }

    /**
     * 参见 <code>InputStream</code> 的 <code>read</code> 方法的一般约定。
     *
     * @return     下一个字节的数据；如果已到达流末尾，则返回 <code>-1</code>。
     * @exception  IOException  如果通过调用其 {@link #close()} 方法关闭了此输入流，
     *                         或者发生 I/O 错误。
     * @see        java.io.FilterInputStream#in
     */
    public synchronized int read() throws IOException {
        if (pos >= count) {
            fill();
            if (pos >= count)
                return -1;
        }
        return getBufIfOpen()[pos++] & 0xff;
    }

    /**
     * 将字节读入数组的一部分，必要时最多从底层流读取一次。
     */
    private int read1(byte[] b, int off, int len) throws IOException {
        int avail = count - pos;
        if (avail <= 0) {
            /* 如果请求的长度至少与缓冲区一样大，
               并且没有标记/重置活动，则不必将字节复制到本地缓冲区。
               这样缓冲流将无害地级联。 */
            if (len >= getBufIfOpen().length && markpos < 0) {
                return getInIfOpen().read(b, off, len);
            }
            fill();
            avail = count - pos;
            if (avail <= 0) return -1;
        }
        int cnt = (avail < len) ? avail : len;
        System.arraycopy(getBufIfOpen(), pos, b, off, cnt);
        pos += cnt;
        return cnt;
    }

    /**
     * 从此字节输入流读取字节到指定的字节数组，从给定偏移量开始存储。
     *
     * <p> 此方法实现了 {@link InputStream} 类中对应的
     * {@link InputStream#read(byte[], int, int) read} 方法的一般约定。
     * 为方便起见，它通过反复调用底层流的 {@code read} 方法，尽可能多地读取字节。
     * 这种迭代的 {@code read} 会持续进行，直到满足以下条件之一：<ul>
     *
     *   <li> 已读取指定数量的字节，
     *
     *   <li> 底层流的 {@code read} 方法返回 {@code -1}，表示到达文件末尾，或
     *
     *   <li> 底层流的 {@code available} 方法返回 0，表示进一步的输入请求将被阻塞。
     *
     * </ul> 如果底层流的第一次 {@code read} 返回 {@code -1} 表示文件末尾，则此方法返回
     * {@code -1}。否则，此方法返回实际读取的字节数。
     *
     * <p> 鼓励（但不要求）此类的子类以相同方式尽可能多地读取字节。
     *
     * @param      b     目标缓冲区。
     * @param      off   开始存储字节的偏移量。
     * @param      len   要读取的最大字节数。
     * @return     读取的字节数；如果已到达流末尾，则返回 {@code -1}。
     * @exception  IOException  如果通过调用其 {@link #close()} 方法关闭了此输入流，
     *                         或者发生 I/O 错误。
     */
    public synchronized int read(byte b[], int off, int len)
        throws IOException
    {
        getBufIfOpen(); // 检查流是否已关闭
        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int n = 0;
        for (;;) {
            int nread = read1(b, off + n, len - n);
            if (nread <= 0)
                return (n == 0) ? nread : n;
            n += nread;
            if (n >= len)
                return n;
            // 如果未关闭但没有可用字节，则返回
            InputStream input = in;
            if (input != null && input.available() <= 0)
                return n;
        }
    }

    /**
     * 参见 {@link InputStream} 的 {@code skip} 方法的一般约定。
     *
     * @exception  IOException  如果流不支持定位，
     *                         或者通过调用其 {@link #close()} 方法关闭了此输入流，
     *                         或者发生 I/O 错误。
     */
    public synchronized long skip(long n) throws IOException {
        getBufIfOpen(); // 检查流是否已关闭
        if (n <= 0) {
            return 0;
        }
        long avail = count - pos;

        if (avail <= 0) {
            // 如果未设置标记位置，则不在缓冲区中保留
            if (markpos <0)
                return getInIfOpen().skip(n);

            // 填充缓冲区以保存字节用于重置
            fill();
            avail = count - pos;
            if (avail <= 0)
                return 0;
        }

        long skipped = (avail < n) ? avail : n;
        pos += skipped;
        return skipped;
    }

    /**
     * 返回在不阻塞的情况下，下次调用此输入流方法时可读取（或跳过）的字节数的估计值。
     * 下次调用可能是同一线程或另一线程。单次读取或跳过这么多字节不会阻塞，但可能读取或跳过更少的字节。
     * <p>
     * 此方法返回缓冲区中剩余可读字节数（{@code count - pos}）与调用
     * {@link java.io.FilterInputStream#in in}.available() 的结果之和。
     *
     * @return     可从此输入流读取（或跳过）而不阻塞的字节数的估计值。
     * @exception  IOException  如果通过调用其 {@link #close()} 方法关闭了此输入流，
     *                         或者发生 I/O 错误。
     */
    public synchronized int available() throws IOException {
        int n = count - pos;
        int avail = getInIfOpen().available();
        return n > (Integer.MAX_VALUE - avail)
                    ? Integer.MAX_VALUE
                    : n + avail;
    }

    /**
     * 参见 {@link InputStream} 的 {@code mark} 方法的一般约定。
     *
     * @param   readlimit   在标记位置失效前可读取的最大字节数。
     * @see     java.io.BufferedInputStream#reset()
     */
    public synchronized void mark(int readlimit) {
        marklimit = readlimit;
        markpos = pos;
    }

    /**
     * 参见 {@link InputStream} 的 {@code reset} 方法的一般约定。
     * <p>
     * 如果 {@code markpos} 为 {@code -1}（未设置标记或标记已失效），
     * 则抛出 {@code IOException}。否则，将 {@code pos} 设置为等于 {@code markpos}。
     *
     * @exception  IOException  如果此流未被标记，或标记已失效，
     *                  或通过调用其 {@link #close()} 方法关闭了流，
     *                  或者发生 I/O 错误。
     * @see        java.io.BufferedInputStream#mark(int)
     */
    public synchronized void reset() throws IOException {
        getBufIfOpen(); // 如果已关闭则抛出异常
        if (markpos < 0)
            throw new IOException("Resetting to invalid mark");
        pos = markpos;
    }

    /**
     * 测试此输入流是否支持 {@code mark} 和 {@code reset} 方法。
     * {@code BufferedInputStream} 的 {@code markSupported} 方法返回 {@code true}。
     *
     * @return  {@code boolean}，指示此流类型是否支持 {@code mark} 和 {@code reset} 方法。
     * @see     java.io.InputStream#mark(int)
     * @see     java.io.InputStream#reset()
     */
    public boolean markSupported() {
        return true;
    }

    /**
     * 关闭此输入流并释放与该流关联的所有系统资源。
     * 一旦流被关闭，后续对 {@code read()}、{@code available()}、{@code reset()} 或 {@code skip()} 的调用将抛出 IOException。
     * 关闭已关闭的流不会产生任何效果。
     *
     * @exception  IOException  如果发生 I/O 错误。
     */
    public void close() throws IOException {
        byte[] buffer;
        while ( (buffer = buf) != null) {
            if (bufUpdater.compareAndSet(this, buffer, null)) {
                InputStream input = in;
                in = null;
                if (input != null)
                    input.close();
                return;
            }
            // 否则重试，以防在 fill() 中有新的缓冲区被 CAS 操作
        }
    }
}
